#include "gameClient.h"

#include "../command/clientSideCommand.h"
#include "../client/clientSideClient.h"

#include <vector>
using namespace std;


GameClient* game;
bool keys[256];

GameClient::GameClient(const char* serverIP)
{
	//ip
	mServerIP = serverIP;

	//client
	networkClient	= new dreamClient;
 	mLocalClient		= NULL;
 	mInputClient = new ClientSideClient();

	//rendering
	mFrameTime		= 0.0f;
 	mRenderTime		= 0.0f;

	//game
	mInitialize			= false;

	//serverPlayer
	mServerPlayerToggle = false;

	//command
	mCommandBuffer = 5;
	mClientRealTime = 0.0f;
 }

 GameClient::~GameClient()
 {
 	delete networkClient;
 }


void GameClient::createPlayer(ClientSideClient* clientSideClient)
{
	Ogre::Entity* NinjaEntity = mSceneMgr->createEntity("ninja.mesh");
	Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    node->attachObject(NinjaEntity);
	clientSideClient->myNode = node;
}
void GameClient::createServerPlayer(ClientSideClient* clientSideClient)
{
    Ogre::Entity* NinjaEntity = mSceneMgr->createEntity("ninja.mesh");
    Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
    node->attachObject(NinjaEntity);
    clientSideClient->myServerNode = node;
}
//-------------------------------------------------------------------------------------
void GameClient::createScene(void)
{
    mSceneMgr->setAmbientLight(Ogre::ColourValue(0.75, 0.75, 0.75));

    Ogre::Light* pointLight = mSceneMgr->createLight("pointLight");
    pointLight->setType(Ogre::Light::LT_POINT);
    pointLight->setPosition(Ogre::Vector3(250, 150, 250));
    pointLight->setDiffuseColour(Ogre::ColourValue::White);
    pointLight->setSpecularColour(Ogre::ColourValue::White);
}
//-------------------------------------------------------------------------------------
bool GameClient::processUnbufferedInput(const Ogre::FrameEvent& evt)
{
    if (mKeyboard->isKeyDown(OIS::KC_I)) // Forward
    {
		keys[VK_UP] = TRUE;
    }
	else
	{
        keys[VK_UP] = FALSE;
	}
    if (mKeyboard->isKeyDown(OIS::KC_K)) // Backward
    {
		keys[VK_DOWN] = TRUE;
    }
	else
	{
        keys[VK_DOWN] = FALSE;
	}
    if (mKeyboard->isKeyDown(OIS::KC_J)) // Left - yaw or strafe
    {
		keys[VK_LEFT] = TRUE;
    }
	else
	{
        keys[VK_LEFT] = FALSE;
	}
    if (mKeyboard->isKeyDown(OIS::KC_L)) // Right - yaw or strafe
    {
		keys[VK_RIGHT] = TRUE;
    }
	else
	{
        keys[VK_RIGHT] = FALSE;
	}

	if (mKeyboard->isKeyDown(OIS::KC_V)) // toggle serverPlayerMovement
	{
		mServerPlayerToggle = !mServerPlayerToggle;
	}

    return true;
}
//-------------------------------------------------------------------------------------
bool GameClient::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
	float timeInMsc = evt.timeSinceLastFrame * 1000;
	mClientRealTime += timeInMsc;

	bool ret = BaseApplication::frameRenderingQueued(evt);

    if(!processUnbufferedInput(evt)) return false;

	if(game != NULL)
	{
		game->CheckKeys();
		game->RunNetwork(timeInMsc);
		mRenderTime = evt.timeSinceLastFrame;


	}
    return ret;
}

 void GameClient::Shutdown(void)
 {
 	Disconnect();
 }

ClientSideClient *GameClient::GetClientPointer(int index)
{
	for (int i = 0; i < mClientVector.size(); i++)
 	{
		if(mClientVector.at(i)->index == index)
 			return mClientVector.at(i);
 	}
 	return NULL;
}

void GameClient::CheckKeys(void)
{
	mInputClient->command.mKey = 0;
 	if(keys[VK_ESCAPE])
 	{
 		Shutdown();
 		keys[VK_ESCAPE] = false;
 	}
 	if(keys[VK_DOWN])
 	{
 		mInputClient->command.mKey |= KEY_DOWN;
 	}
 	if(keys[VK_UP])
 	{
 		mInputClient->command.mKey |= KEY_UP;
 	}
 	if(keys[VK_LEFT])
 	{
 		mInputClient->command.mKey |= KEY_LEFT;
 	}
 	if(keys[VK_RIGHT])
 	{
 		mInputClient->command.mKey |= KEY_RIGHT;
 	}
 	mInputClient->command.mMilliseconds = (int) (mFrameTime * 1000);
}

void GameClient::MoveServerPlayer(void)
{
	if(!mLocalClient)
		return;

    Ogre::Vector3 transVector = Ogre::Vector3::ZERO;

	transVector.x = mLocalClient->mCommandVector.back()->mOrigin.x;
    transVector.y = mLocalClient->mCommandVector.back()->mOrigin.y;

	if (mServerPlayerToggle)
		mLocalClient->myServerNode->setPosition(transVector);
}

void GameClient::StartConnection()
{
	int ret = networkClient->Initialise("", mServerIP, 30006);

	if(ret == DREAMSOCK_CLIENT_ERROR)
	{
		char text[64];
		sprintf(text, "Could not open client socket");
	}
	Connect();
}

void GameClient::ReadPackets(void)
{
	char data[1400];
	struct sockaddr address;

	ClientSideClient *clList;

	int type;
	int ind;
	int local;
	int ret;

	char name[50];

	dreamMessage mes;
	mes.Init(data, sizeof(data));

	while(ret = networkClient->GetPacket(mes.data, &address))
	{
		mes.SetSize(ret);
		mes.BeginReading();

		type = mes.ReadByte();

		switch(type)
		{
		case DREAMSOCK_MES_ADDCLIENT:
			local	= mes.ReadByte();
			ind		= mes.ReadByte();
			strcpy(name, mes.ReadString());

			AddClient(local, ind, name);
			break;

		case DREAMSOCK_MES_REMOVECLIENT:
			ind = mes.ReadByte();

			LogString("Got removeclient %d message", ind);

			RemoveClient(ind);

			break;

		case USER_MES_FRAME:
			// Skip sequences
			mes.ReadShort();
			mes.ReadShort();

			for (int i = 0; i < mClientVector.size(); i++)
			{
				ReadDeltaMoveCommand(&mes, mClientVector.at(i));
			}

			break;

		case USER_MES_NONDELTAFRAME:
			// Skip sequences
			mes.ReadShort();
			mes.ReadShort();

			for (int i = 0; i < mClientVector.size(); i++)
			{
				LogString("Reading NONDELTAFRAME for client %d", mClientVector.at(i)->index);
				ReadMoveCommand(&mes, mClientVector.at(i));
			}

			break;

		case USER_MES_SERVEREXIT:
			//MessageBox(NULL, "Server disconnected", "Info", MB_OK);
			Disconnect();
			break;

		}
	}
}
void GameClient::AddClient(int local, int ind, char *name)
{
	ClientSideClient* clientSideClient = new ClientSideClient();
	mClientVector.push_back(clientSideClient);
	createPlayer(clientSideClient);

	if(local)
	{
		mLocalClient = clientSideClient;
		createServerPlayer(clientSideClient);
		SendRequestNonDeltaFrame();
	}

	//fill up this clients mClientVector... put another way buffer up some non moves so we can just start
	//processing ticks.

	for (int i = 0; i < mCommandBuffer; i++)
	{
		ClientSideCommand* clientSideCommand = new ClientSideCommand();
		clientSideClient->mCommandVector.push_back(clientSideCommand);
	}

}

void GameClient::RemoveClient(int ind)
{
	/*
	for (int i = 0; i < mClientVector.size(); i++)
	{
		if (mClientVector.at(i)->index == ind)
		{
			mClientVector.erase(mClientVector.begin()+i);
			return;
		}
	}
	*/
}

void GameClient::SendCommand(void)
{
	if(networkClient->GetConnectionState() != DREAMSOCK_CONNECTED)
		return;

	dreamMessage message;
	char data[1400];

	int i = networkClient->GetOutgoingSequence() & (COMMAND_HISTORY_SIZE-1);

	message.Init(data, sizeof(data));
	message.WriteByte(USER_MES_FRAME);						// type
	message.AddSequences(networkClient);					// sequences

	// Build delta-compressed move command
	BuildDeltaMoveCommand(&message, mInputClient);

	// Send the packet
	networkClient->SendPacket(&message);

	// Store the command to the input client's history
	memcpy(&mInputClient->frame[i], &mInputClient->command, sizeof(ClientSideCommand));

	// Store the commands to the clients' history
	for (int i = 0; i < mClientVector.size(); i++)
	{
		memcpy(&mClientVector.at(i)->frame[i], &mClientVector.at(i)->command, sizeof(ClientSideCommand));
	}
}

void GameClient::SendRequestNonDeltaFrame(void)
{
	char data[1400];
	dreamMessage message;
	message.Init(data, sizeof(data));

	message.WriteByte(USER_MES_NONDELTAFRAME);
	message.AddSequences(networkClient);

	networkClient->SendPacket(&message);
}

void GameClient::Connect(void)
{
	if(mInitialize)
	{
		LogString("ArmyWar already mInitializeialised");
		return;
	}

	LogString("GameClient::Connect");

	mInitialize = true;

	networkClient->SendConnect("myname");
}

void GameClient::Disconnect(void)
{
	if(!mInitialize)
		return;

	LogString("GameClient::Disconnect");

	mInitialize = false;
	mLocalClient = NULL;
	mInputClient = NULL;

	networkClient->SendDisconnect();
}
void GameClient::ReadMoveCommand(dreamMessage *mes, ClientSideClient *client)
{
	ClientSideCommand *clientSideCommand = new ClientSideCommand();
	client->mCommandVector.push_back(clientSideCommand);

	clientSideCommand->mKey			 = mes->ReadByte();

	clientSideCommand->mOrigin.x	 = mes->ReadFloat();
	clientSideCommand->mOrigin.y	 = mes->ReadFloat();

	clientSideCommand->mVelocity.x	 = mes->ReadFloat();
	clientSideCommand->mVelocity.y	 = mes->ReadFloat();

	clientSideCommand->mServerRealTime     = mes->ReadShort();
	clientSideCommand->mServerFrameNumber  = mes->ReadLong();
	//LogString("ReadMoveFrame:%d",clientSideCommand->mServerFrameNumber);

	clientSideCommand->mMilliseconds = mes->ReadByte();

}
void GameClient::ReadDeltaMoveCommand(dreamMessage *mes, ClientSideClient *client)
{
	ClientSideCommand *clientSideCommand = new ClientSideCommand();

	int processedFrame;
	int flags = 0;

	// Flags
	flags = mes->ReadByte();

	// Key
	if(flags & CMD_KEY)
	{
		clientSideCommand->mKey = mes->ReadByte();
		LogString("Client %d: Read key %d", client->index, clientSideCommand->mKey);
	}

	if(flags & CMD_ORIGIN)
	{
		processedFrame = mes->ReadByte();
	}

	//just feed into raw data into vector as it comes and we will deal with it elsewhere.
	clientSideCommand->mOrigin.x     = mes->ReadFloat();
	clientSideCommand->mOrigin.y     = mes->ReadFloat();

	clientSideCommand->mVelocity.x   = mes->ReadFloat();
	clientSideCommand->mVelocity.y  = mes->ReadFloat();

	clientSideCommand->mServerRealTime     = mes->ReadShort();
	LogString("mServerRealTime:%d",	clientSideCommand->mServerRealTime);
	clientSideCommand->mServerFrameNumber  = mes->ReadLong();
	LogString("mServerFrameNumber:%d",	clientSideCommand->mServerFrameNumber);

	clientSideCommand->mMilliseconds = mes->ReadByte();

	//This is the elsewhere to deal with vector mentioned above.
	clientSideCommand->mClientTimeSinceLastPacket = mClientRealTime -
		client->mCommandVector.at(client->mCommandVector.size() - 2)->mClientRealTime;

	if (clientSideCommand->mServerRealTime > client->mCommandVector.back()->mServerRealTime || clientSideCommand->mServerRealTime < -32000 && client->mCommandVector.back()->mServerRealTime > 32000)
	{
		client->mCommandVector.push_back(clientSideCommand);
		ProcessTick(client);
	}
	else
	{
	}
}


void GameClient::ProcessTick(ClientSideClient *client)
{
	ClientSideCommand* commandToRun = client->mCommandVector.back();

	//set velocity needed to get to server point in one frame
	client->command.mVelocity.x = commandToRun->mOrigin.x - client->myNode->getPosition().x;
    client->command.mVelocity.y = commandToRun->mOrigin.y - client->myNode->getPosition().y;

	//set heading and normalise it
	client->command.mHeading.x = client->command.mVelocity.x;
	client->command.mHeading.y = client->command.mVelocity.y;

	client->command.mHeading.normalise();

}

//call this every render frame
void GameClient::interpolateTick(ClientSideClient *client)
{
    float interpolation_constant = .1;

    int size = client->mCommandVector.size();

	//check to see if there was no move on last command....
	if (client->mCommandVector.back()->mOrigin.x == client->mCommandVector.at(size - 2)->mOrigin.x &&
             client->mCommandVector.back()->mOrigin.y == client->mCommandVector.at(size - 2)->mOrigin.y)
    {
		// just don't translate....
	}
    else
    {
		client->myNode->translate(client->command.mHeading * mRenderTime * interpolation_constant * 1000, Ogre::Node::TS_WORLD);
    }
}

void GameClient::CalculateVelocity(ClientSideCommand *command, float frametime)
{

	float multiplier = 100.0f;

	command->mVelocity.x = 0.0f;
	command->mVelocity.y = 0.0f;

	if(command->mKey & KEY_UP)
	{

		command->mVelocity.y += multiplier * frametime;
	}

	if(command->mKey & KEY_DOWN)
	{
		command->mVelocity.y += -multiplier * frametime;
	}

	if(command->mKey & KEY_LEFT)
	{
		command->mVelocity.x += -multiplier * frametime;
	}

	if(command->mKey & KEY_RIGHT)
	{
		command->mVelocity.x += multiplier * frametime;
	}
}

void GameClient::RunNetwork(int msec)
{
	static int time = 0;
	time += msec;

	MoveServerPlayer();

	for (int i = 0; i < mClientVector.size(); i++)
	{
		if (mClientVector.at(i)->mCommandVector.size() > mCommandBuffer)
		{
			interpolateTick(mClientVector.at(i));
		}
	}

	// Framerate is too high
	if(time < (1000 / 60)) {
		return;
	}

	mFrameTime = time / 1000.0f;
	time = 0;

	// Read packets from server, and send new commands
	ReadPackets();
	SendCommand();
}

void GameClient::BuildDeltaMoveCommand(dreamMessage *mes, ClientSideClient *theClient)
{
	int flags = 0;
	int last = (networkClient->GetOutgoingSequence() - 1) & (COMMAND_HISTORY_SIZE-1);

	// Check what needs to be updated
	if(theClient->frame[last].mKey != theClient->command.mKey)
		flags |= CMD_KEY;

	// Add to the message
	// Flags
	mes->WriteByte(flags);

	// Key
	if(flags & CMD_KEY)
	{
		mes->WriteByte(theClient->command.mKey);
	}

	mes->WriteByte(theClient->command.mMilliseconds);
}


#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
    int main(int argc, char *argv[])
#endif
    {

        //ClientSideBaseGame* mClientSideBaseGame;
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        game = new GameClient(strCmdLine);
#else
        game = new GameClient(argv[1]);
#endif

		//game = new GameClient;
	    game->StartConnection();

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		StartLogConsole();
#endif

        try {
            game->go();
        } catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
            std::cerr << "An exception has occured: " <<
                e.getFullDescription().c_str() << std::endl;
#endif
        }

        return 0;
    }

#ifdef __cplusplus
}
#endif